using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Threading;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Markup;
using System.Windows.Threading;
using ViewOnCore.Helper.WPF;
using ViewOnCore.HostContract.Media;

using MediaDesktopPlayerAddIn.Properties;

namespace UI.Control
{

    // Not out of screen when displayed
    public partial class VideoWindow : System.Windows.Controls.UserControl
    {

        #region Variables

        internal Video Video;

        private bool _isAutoStartedOnLoad = false;
        private TimeSpan _lastPosition = TimeSpan.MinValue; // Used to handle a bug in MediaElement

        //---- Inactivity management
        static private int _inactivityPeriod = 4000;

        private readonly System.Threading.Timer _mouseTimer;
        private bool _isInactive = false;
        private bool _isInactivityOn = false;

        //----
        private bool _isPlaying = false;

        //----
        private Timer _timer;

        private bool _isInTimerCallback = false;

        #endregion

        #region Constructor

        public VideoWindow()
        {
            InitializeComponent();
        }

        public VideoWindow(Video video)
        {
            InitializeComponent();

            Video = video;

            childWindowManager.VisuallyReady += new EventHandler(childWindowManager_VisuallyReady);
            childWindowManager.Closing += new System.ComponentModel.CancelEventHandler(childWindowManager_Closing);

            //---- Inactivity management
            _mouseTimer = new System.Threading.Timer(new System.Threading.TimerCallback(InactivityCheck), null, _inactivityPeriod, -1);
            MouseMove += new MouseEventHandler(VideoWindow_MouseMove);
            IsInactivityOn = true;

            //----
            btnFullScreen.IsEnabled = false;
            btnPrevious.IsEnabled = false;
            btnPlay.IsEnabled = false;
            sliPosition.IsEnabled = false;
            sliVolume.IsEnabled = false;
            btnMute.IsEnabled = false;
            txtProgress.IsEnabled = false;
            mediaElement.IsEnabled = false;

            //----
            btnPlay.Click += new RoutedEventHandler(btnPlay_Click);
            btnMute.Click += new RoutedEventHandler(btnMute_Click);
            btnPrevious.Click += new RoutedEventHandler(btnPrevious_Click);
            btnFullScreen.Click += new RoutedEventHandler(btnFullScreen_Click);
            sliPosition.ValueChanged += new RoutedPropertyChangedEventHandler<double>(sliPosition_ValueChanged);
            sliVolume.ValueChanged += new RoutedPropertyChangedEventHandler<double>(sliVolume_ValueChanged);

            this.MouseDoubleClick += new MouseButtonEventHandler(VideoWindow_MouseDoubleClick);

            //----
            mediaElement.MediaOpened += new RoutedEventHandler(mediaElement_MediaOpened);
            mediaElement.MediaFailed += new EventHandler<ExceptionRoutedEventArgs>(mediaElement_MediaFailed);
            mediaElement.MediaEnded += new RoutedEventHandler(mediaElement_MediaEnded);

            //---- Timer
            _timer = new Timer(new TimerCallback(TimerCallback));
            _timer.Change(-1, -1);
        }

        void childWindowManager_VisuallyReady(object sender, EventArgs e)
        {
            if (_isAutoStartedOnLoad)
            {
                if (_lastPosition != TimeSpan.MinValue)
                    mediaElement.Position = _lastPosition;
                return;
            }
            _isAutoStartedOnLoad = true;

            Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
                                    (DelegateInvoker)delegate { AsyncSetup(); });
        }

        private void AsyncSetup()
        {
            mediaElement.Source = new Uri(Video.Path);

            // Set up
            sliVolume.Value = sliVolume.Maximum * mediaElement.Volume;

            // Play
            btnPlay_Click(null, null);
        }

        void mediaElement_MediaFailed(object sender, ExceptionRoutedEventArgs e)
        {
            mediaElement.MediaOpened -= new RoutedEventHandler(mediaElement_MediaOpened);
            mediaElement.MediaFailed -= new EventHandler<ExceptionRoutedEventArgs>(mediaElement_MediaFailed);

            txtError.Text = GetErrorMessage(e.ErrorException.Message);

            txtError.Visibility = Visibility.Visible;
            mediaElement.Stop();
        }

        void mediaElement_MediaOpened(object sender, RoutedEventArgs e)
        {
            if (mediaElement.NaturalVideoWidth < 1)
                return;

            this.Width = mediaElement.NaturalVideoWidth;
            this.Height = mediaElement.NaturalVideoHeight;

            _timer.Change(500, 500);

            btnFullScreen.IsEnabled = true;
            btnPrevious.IsEnabled = true;
            btnPlay.IsEnabled = true;
            sliPosition.IsEnabled = true;
            sliVolume.IsEnabled = true;
            btnMute.IsEnabled = true;
            txtProgress.IsEnabled = true;
            mediaElement.IsEnabled = true;

            mediaElement.MediaOpened -= new RoutedEventHandler(mediaElement_MediaOpened);
        }

        // http://blogs.msdn.com/joshpoley/pages/errors-00d-facility-mediaserver.aspx
        private string GetErrorMessage(string message)
        {
            if (message.Contains("0xC00D10D1"))
                return Globalization.VideoWindow_NoCodec;

            return Globalization.VideoWindow_UnableToLoadVideo;
        }

        #endregion

        #region Inactivity management : Auto hide

        /// <summary>
        /// Does we check for inactivity now ?
        /// </summary>
        public bool IsInactivityOn
        {
            get
            {
                return _isInactivityOn;
            }
            set
            {
                _isInactivityOn = value;
                if (!_isInactivityOn)
                    SetMouseActive();
            }
        }

        private delegate void HideMouseHandler(object state);
        private void InactivityCheck(object state)
        {
            if (!base.Dispatcher.CheckAccess())
                base.Dispatcher.Invoke(DispatcherPriority.Send, new HideMouseHandler(InactivityCheck), state);
            else
                SetMouseInactive();
        }

        void VideoWindow_MouseMove(object sender, MouseEventArgs e)
        {
            //double mouseX = e.GetPosition(this).X;
            //double mouseY = e.GetPosition(this).Y;

            //if (mouseY < 0 || mouseY > ActualHeight || mouseX < 0 || mouseX > ActualWidth)
            //{
            //    _mouseTimer.Change(_inactivityPeriod, -1);
            //    return;
            //}

            //if (mouseY > this.ActualHeight - 30 && mouseY < ActualHeight && mouseX > 0 && mouseX < ActualWidth)
            //{
            //    SetMouseActive();
            //    _mouseTimer.Change(-1, -1);
            //    return;
            //}

            //bool isMouseOverBar = false; // (mouseY > this.ActualHeight - 30 && mouseY < ActualHeight && mouseX > 0 && mouseX < ActualWidth);
            //_mouseTimer.Change(isMouseOverBar ? -1 : _inactivityPeriod, -1);

            _mouseTimer.Change(_inactivityPeriod, -1);
            SetMouseActive();
        }

        private void SetMouseInactive()
        {
            if (!_isInactivityOn || _isInactive)
                return;

            double mouseX = Mouse.GetPosition(this).X;
            double mouseY = Mouse.GetPosition(this).Y;
            if (mouseY > ActualHeight - 30 && mouseY < ActualHeight && mouseX > 0 && mouseX < ActualWidth)
                return;

            _isInactive = true;
            //DeactivateScreenSaver = true;
            base.Cursor = Cursors.None;

            childWindowManager.SetMouseInactive();

            Storyboard storyboard = (Storyboard)FindResource("SetMouseInactiveInner");
            storyboard.Begin(this);
        }

        private void SetMouseActive()
        {
            if (!_isInactivityOn || !_isInactive)
                return;

            //DeactivateScreenSaver = false;

            base.Cursor = Cursors.Arrow;

            childWindowManager.SetMouseActive();

            Storyboard storyboard = (Storyboard)FindResource("SetMouseActiveInner");
            storyboard.Begin(this);

            _isInactive = false;
        }

        #endregion

        #region StandBy

        public void StandBy()
        {
            if (_isPlaying)
                btnPlay_Click(null, null);

            _lastPosition = mediaElement.Position;
        }

        #endregion

        #region Play / Pause

        void btnPlay_Click(object sender, RoutedEventArgs e)
        {
            _isPlaying = !_isPlaying;

            if (_isPlaying)
            {
                _timer.Change(500, 500);
                mediaElement.Play();

                IsInactivityOn = true;

                patPause.Visibility = Visibility.Visible;
                patPlay.Visibility = Visibility.Collapsed;
            }
            else
            {
                _timer.Change(-1, -1);
                mediaElement.Pause();

                IsInactivityOn = false;

                patPause.Visibility = Visibility.Collapsed;
                patPlay.Visibility = Visibility.Visible;
            }
        }

        #endregion

        #region Mute

        void btnMute_Click(object sender, RoutedEventArgs e)
        {
            mediaElement.IsMuted = !mediaElement.IsMuted;

            if (mediaElement.IsMuted)
            {
                patMuteOff.Visibility = Visibility.Visible;
                patMuteOn.Visibility = Visibility.Collapsed;
            }
            else
            {
                patMuteOff.Visibility = Visibility.Collapsed;
                patMuteOn.Visibility = Visibility.Visible;
            }
        }

        #endregion

        #region Position

        void sliPosition_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (_isInTimerCallback)
                return;

            double ticks = mediaElement.NaturalDuration.TimeSpan.Ticks * (sliPosition.Value / sliPosition.Maximum);
            mediaElement.Position = new TimeSpan((long)ticks);
        }

        void btnPrevious_Click(object sender, RoutedEventArgs e)
        {
            sliPosition.Value = 0;
        }

        #endregion

        #region Volume

        void sliVolume_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            mediaElement.Volume = sliVolume.Value / sliVolume.Maximum;
        }

        #endregion

        #region Full screen

        void VideoWindow_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            if (mediaElement.IsEnabled)
                ReverseFullScreen();
        }

        void btnFullScreen_Click(object sender, RoutedEventArgs e)
        {
            ReverseFullScreen();
        }

        private void ReverseFullScreen()
        {
            _lastPosition = mediaElement.Position;

            if (childWindowManager.DetachableWindow == null)
            {
                //---- If normal screen -> switch to full screen
                griFullScreen.Visibility = Visibility.Collapsed;
                griNormalScreen.Visibility = Visibility.Visible;

                childWindowManager.Detach();
                childWindowManager.DetachableWindow.WindowState = WindowState.Maximized;
            }
            else
            {
                //---- If full screen -> switch to normal screen
                griFullScreen.Visibility = Visibility.Visible;
                griNormalScreen.Visibility = Visibility.Collapsed;

                childWindowManager.Attach();
            }

            if (_lastPosition != TimeSpan.MinValue)
                mediaElement.Position = _lastPosition;
        }

        #endregion

        #region Timer callback

        private void TimerCallback(object state)
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                (DelegateInvoker)delegate { AsyncTimerCallback(); });
        }

        private void AsyncTimerCallback()
        {
            _isInTimerCallback = true;
            try
            {
                sliPosition.Value = sliPosition.Maximum * (((double)mediaElement.Position.Ticks) / mediaElement.NaturalDuration.TimeSpan.Ticks);

                txtProgress.Text = mediaElement.Position.Hours + ":" + mediaElement.Position.Minutes + ":" + mediaElement.Position.Seconds;
                txtProgress.Text += "/" + mediaElement.NaturalDuration.TimeSpan.Hours + ":" + mediaElement.NaturalDuration.TimeSpan.Minutes + ":" + mediaElement.NaturalDuration.TimeSpan.Seconds;
            }
            catch
            {
                // WPF bug
                // "Unable to return a TimeSpan property value for a Duration value of 'Automatic'. Check the HasTimeSpan property before requesting the TimeSpan property value from a Duration."
            }
            _isInTimerCallback = false;
        }

        #endregion

        #region MediaEnded

        void mediaElement_MediaEnded(object sender, RoutedEventArgs e)
        {
            sliPosition.Value = 0;

            _isPlaying = false;
            _timer.Change(-1, -1);
            mediaElement.Pause();

            patPause.Visibility = Visibility.Collapsed;
            patPlay.Visibility = Visibility.Visible;
        }

        #endregion

        #region Event : Closing

        void childWindowManager_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            // if Full-screen
            if (childWindowManager.DetachableWindow != null)
            {
                ReverseFullScreen();
                e.Cancel = true;
            }
        }

        #endregion

    }

}